Local File Inclusion
略称: LFI
ローカルのファイルの内容を通信などの出力に含ませてしまう脆弱性
要はサーバーのファイルを外部から読める脆弱性
PHPで任意のローカルファイルをincludeしてしまう脆弱性のことも指す 例
code:server.py
from flask import Flask, request, send_file
app = Flask(__name__)
@app.get("/page")
def page():
file = request.args.get("file", "index.html")
return send_file("./pages/" + file)
if __name__ == "__main__":
app.run()
/page?file=hoge.htmlで./pages/hoge.htmlが送信される
fileの値を操作することにより、任意のファイルを外部から読むことができる
/page?file=../../../../../../../etc/passwdで/etc/passwdが送信される
code:/etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
対策
テクニック
Range Header
サーバーがRangeヘッダに対応している場合、範囲を指定してファイルの一部を読み込むことができる
一度に読み込めるファイルのサイズが決められていたり、フィルタがある場合に有効
よく使われるファイル
/proc
Linuxのプロセスに関する情報が入っている特殊なディレクトリ
/proc/self/environ
LFIから環境変数を漏洩できる
自身のプロセスが起動したときの環境変数の値が書かれている
/proc/self/maps
LFIをテストするときのセンシティブでないファイルとして扱われているっぽい
自身のプロセスのメモリマップが書かれている
/proc/self/mem
自身のプロセスのメモリを読むことができる
読み込むときはseekでアドレスを合わせないといけない
実際のメモリマップのように扱われる
Rangeヘッダが有効のとき、Rangeでアドレスの範囲を指定することで/proc/self/memを読める場合がある (実装依存)
/proc/self/fd/{num}
自身のプロセスが持つファイルディスクリプタが開いているファイルへのシンボリックリンク
/dev/fd/{num}もある
/proc/self/cwd
自身のプロセスのカレントディレクトリへのシンボリックリンク
/proc/self/cmdline
自身のプロセスを実行したコマンドの文字列
引数で機密情報を渡してたりするとこれで覗ける
/proc/self/root
/へのシンボリックリンク
/dev/stdin
標準入力を指すファイル
/dev/stdout, /dev/stderrもそれぞれある
/proc/self/fd/0や/dev/pts, /dev/ttyもある
/etc/passwd
ユーザーに関する情報が書かれている
LFIの確認に使われる
/etc/shadow
ユーザーのパスワードハッシュが入っている
通常root権限が必要
ブルートフォースなどでハッシュを復元できればSSHで接続できるかもしれない
/home/{user}/.bash_history
bashのコマンド履歴が入っている
面白い情報があるかもしれない
/home/{user}/.ssh/id_rsa
sshの秘密鍵があるかもしれない
シンボリックリンクを考慮しないパス正規化の悪用
/dev/fd/../environを単純に正規化すると/dev/environになる
しかし/dev/fdは/proc/self/fdへのシンボリックリンクなので、実際はこのパスは/proc/self/environを指す
この挙動を使ってFilter bypassができる
ヌルバイト攻撃
file + ".png"のように末尾に操作できない文字列が追加される場合、自由にLFIができない
環境によっては、/?file=/etc/passwd%00のようなリクエストを送ると文字列がNULL文字で終わるようになるため、/etc/passwdをLFIできる
PHP 5.4未満ではこのようなことが可能だったらしい
具体的な攻撃テクニックなどはこちらを参照
CVE
資料